<?php
declare(strict_types=1);


namespace Reven\Web3;

use kornrunner\Ethereum\Transaction;
use Reven\Result\Re;
use Web3\Contract;
use Web3\Eth;
use Web3\Providers\HttpProvider;
use Web3\RequestManagers\HttpRequestManager;
use Web3\Utils;
use Web3\Web3;

class EtherInstance extends Help
{
    /**
     * @var string
     */
    protected $url;


    /**
     * @var int
     */
    protected $chId;

    /**
     * @var Web3
     */
    protected $web3;

    /**
     * @var Eth
     */
    protected $eth;

    public function __construct(string $url,int $chId)
    {
       $this->resetWeb3($url,$chId);
    }

    /**
     * @param string $url
     * @param int $chId
     */
    public function resetWeb3(string $url,int $chId){
        $this->url=$url;
        $this->web3=new Web3(new HttpProvider(new HttpRequestManager($url,30)));
        $this->eth=$this->web3->getEth();
        $this->chId=$chId;
    }

    public function getW3():Web3{
        return $this->web3;
    }

    /**
     * eth_blockNumber
     * @return Re
     */
    public function eth_blockNumber():Re{
        $res=self::reFail();
        $this->eth->blockNumber(function ($err,$re)use(&$res){
            $res=self::callBack($err,$re);
        });

        if ($res->status){
            $res->data=self::BigIntegerToStr($res->data);
        }
        return $res;
    }

    /**
     * 块交易
     * @param int $block
     * @param bool $info
     * @return Re  data.item.input=='0x' ?'契约交易':'燃油交易'
     */
    public function eth_getBlockByNumber(int $block,bool $info=true):Re{
        $res=self::reFail();
        $this->eth->getBlockByNumber(Utils::toHex($block,true),$info,function ($err,$re)use(&$res){
            $res=self::callBack($err,$re);
        });

        if ($res->status&&isset($res->data->transactions)&&!empty($res->data->transactions)){
            $res->data->transactions=$this->eth_getBlockTransactionsHandle($res->data->transactions);
        }

        return $res;
    }


    /**
     * 块充值核心处理
     * @param int $block
     * @return Re
     */
    public function eth_Rec_getBlockByNumber(int $block):Re{
        $re=self::eth_getBlockByNumber($block,true);
        if (!$re->status)return $re;

        if (!isset($re->data->transactions)||empty($re->data->transactions)){
            return self::re([]);
        }

        $arr=[];
        foreach ($re->data->transactions as $item){
            $arr[]=[
                'hash'=>$item->hash,
                'from'=>$item->from,
                'to'=>$item->to,
                'failAddress'=>$item->failAddress,
                'contractAddress'=>$item->contractAddress,
            ];
        }
        return self::re($arr);
    }





    private  function eth_getBlockTransactionsHandle(array $transactions):array {
        $arr=[];
        foreach ($transactions as $item){
            $item->failAddress=$item->to;
            $item->contractAddress='';
            if (isset($item->input)&&$item->input=='0x'){
                $arr[]=$item;
                continue;
            }

            $re=$this->decodeTransferInputRecipient($item->input);
            if ($re->status){
                $item->failAddress=$re->data;
                $item->contractAddress=$item->to;
            }

            $arr[]=$item;
        }
        return $arr;
    }

    /**
     * 从交易input解析契约交易收入地址
     * @param $input
     * @return Re
     */
    public function decodeTransferInputRecipient($input) {
        // 去掉 "0x" 前缀，并确保字符串长度为偶数
        $strippedInput = substr($input, 2);
        if (strlen($strippedInput) % 2 !== 0) {
            return self::reFail('Input data length is not even.');
        }

        // 解码函数选择器（前 8 个字符）
        $functionSelector = substr($strippedInput, 0, 8);

        // 确认这是 transfer 函数的选择器
        if ($functionSelector !== 'a9059cbb') {
            return self::reFail('Function selector does not match transfer.');
        }

        // 解码 recipient 地址（接下来 64 个字符）
        $recipientHex = substr($strippedInput, 8, 64);
        $recipient = $this->logs_handleAddress('0x'.$recipientHex);

        return self::re($recipient);
    }

    /**
     * 获取钱包余额
     * @param string $address
     * @param string $contractAddress
     * @param int $numberOfDecimalss
     * @return Re
     */
    public function getBalance(string $address,string $contractAddress='',int $numberOfDecimalss=18):Re{
        return $contractAddress?$this->contract_balanceOf($contractAddress,$address):$this->eth_getBalance($address,$numberOfDecimalss);
    }

    /**
     * 燃油币余额
     * @param string $address
     * @param int $numberOfDecimalss
     * @return Re
     */
    public function eth_getBalance(string $address,int $numberOfDecimalss=18):Re{
        try {
            $res=self::reFail();
            $this->eth->getBalance($address,function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });

            if ($res->status){
                $res->data=self::fromWeiToDecimalsString($res->data,$numberOfDecimalss);
            }
            return $res;
        }catch (\Throwable $e){
            return self::reFail($e->getMessage())->setCode('500');
        }
    }

    /**
     * 代币余额
     * @param $contractAddress
     * @param $address
     * @return Re
     */
    public function contract_balanceOf($contractAddress, $address):Re{
        try {
            $abi = '[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}]';
            $contract = new Contract($this->eth->getProvider(), json_decode($abi,true));

            // 使用合约实例调用 balanceOf 方法
            $res=self::reFail();
            $contract->at($contractAddress)->call('balanceOf', $address,function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });
            if (!$res->status)return $res;
            $balanceWei=$res->data['balance']->toString();

            // 获取合约的小数位数
            $res=$this->contract_decimals($contractAddress);
            if (!$res->status)return $res;
            $decimals=$res->data;

            // 将余额转换为人类可读的格式
            $balanceReadable = bcdiv($balanceWei, bcpow('10', $decimals), (int)$decimals);

            return self::re($balanceReadable);
        }catch (\Throwable $e){
            return self::reFail($e->getMessage())->setCode('500');
        }
    }

    protected function contract_decimals(string $contractAddress):Re{
        $abi = '[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}]';
        $contract = new Contract($this->eth->getProvider(), json_decode($abi,true));
        // 获取合约的小数位数
        $res=self::reFail();
        $contract->at($contractAddress)->call('decimals',function ($err,$re)use(&$res){
            $res=self::callBack($err,$re);
        });
        if (!$res->status)return $res;
        $res->data=$res->data[0]->toString();
        return $res;
    }

    /**
     * 代币详情
     * @param string $contractAddress
     * @return Re
     */
    public function constantInfo(string $contractAddress):Re
    {
        try {
            $abi = '[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":true,"inputs":[],"name":"_decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]';;
            $contract = new Contract($this->eth->getProvider(), json_decode($abi,true));
            // 使用合约实例调用 balanceOf 方法
            $res=self::reFail();
            $contract->at($contractAddress)->call('name',function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });
            if (!$res->status)return $res;
            $name=$res->data[0];


            $res=self::reFail();
            $contract->at($contractAddress)->call('symbol',function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });
            if (!$res->status)return $res;
            $symbol=$res->data[0];

            $res=self::reFail();
            $contract->at($contractAddress)->call('decimals',function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });
            if (!$res->status)return $res;
            $decimals=$res->data[0]->toString();

            // 发行总量
            $res=self::reFail();
            $contract->at($contractAddress)->call('totalSupply',function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });
            if (!$res->status)return $res;
            $totalSupply=bcdiv($res->data[0]->toString(), bcpow('10', $decimals), (int)$decimals);

            $res=self::reFail();
            $contract->at($contractAddress)->call('getOwner',function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });
            $owner=$res->status?$res->data[0]:'';

            return self::re([
                'contractAddress'=>$contractAddress,
                'name'=>$name,
                'symbol'=>$symbol,
                'decimals'=>$decimals,
                'owner'=>$owner,
                'totalSupply'=>$totalSupply,
            ]);
        }catch (\Throwable $e){
            return self::reFail($e->getMessage())->setCode('500');
        }
    }


    /**
     * gas limit
     * @param $fromAddress
     * @param $toAddress
     * @param $amount
     * @param null $data
     * @param string $unit
     * @return Re
     */
    protected function eth_estimateGas($fromAddress,$toAddress,$amount,$data=null,$unit='ether'):Re{
        // 设置交易参数
        $txParams = [
            'from' => $fromAddress,
            'to' => $toAddress,
            'value' => !$data?'0x'.Utils::toWei($amount ,$unit)->toHex():'0x0', // 将 ETH 转换为 wei
        ];

        if ($data){
            $txParams['data']=$data;
        }
        $res=self::reFail();
        $this->eth->estimateGas($txParams,function ($err,$re)use(&$res){
            $res=self::callBack($err,$re);
        });
        if (!$res->status)return $res;
        return $res;
    }

    /**
     * gas price
     * @return Re
     */
    protected function eth_gasPrice():Re{
        $res=self::reFail();
        $this->eth->gasPrice(function ($err,$re)use(&$res){
            $res=self::callBack($err,$re);
        });
        if (!$res->status)return $res;

        return $res;
    }


    /**
     * 预估燃油费
     * @param $fromAddress
     * @param $toAddress
     * @param $amount
     * @return Re
     */
    public function gasEmit($fromAddress,$toAddress='0xc664f9c8d8535b58d927938e8ba66a1392d44b4a',$amount='0.00000001'):Re{
        try {
            $res=$this->eth_estimateGas($fromAddress,$toAddress,$amount);
            if (!$res->status)return $res;
            $estimateGas=$res->data->toString();

            $res=$this->eth_gasPrice();
            if (!$res->status)return $res;
            $gasPrice=$res->data->toString();

            // 计算总费用 (Gas 消耗 * Gas 价格)
            $totalCostInWei = bcmul((string)$estimateGas, (string)$gasPrice, 0);

            return self::re( self::weiToNumber(Utils::toHex($totalCostInWei),18));
        }catch (\Throwable $e){
            return self::reFail($e->getMessage())->setCode('500');
        }

    }

    /**
     * eth_nonce
     * @param string $address
     * @return Re
     */
    protected function eth_nonce(string $address):Re{
        // 获取当前 nonce
        $res=self::reFail();
        $this->eth->getTransactionCount($address,function ($err,$re)use(&$res){
            $res=self::callBack($err,$re);
        });
        if (!$res->status)return $res;

        return $res;
    }

    /**
     * 发起交易
     * @param string $fromAddress
     * @param string $fromPrivateKey
     * @param string $toAddress
     * @param string $amount
     * @return Re
     */
    public function transfer(string $fromAddress,string $fromPrivateKey,string $toAddress, string $amount):Re
    {
        try {
            // 获取当前 nonce
            $res=$this->eth_nonce($fromAddress);
            if (!$res->status)return $res;
            $nonce =$res->data->toHex();

            // eth_gasPrice
            $res=$this->eth_gasPrice();
            if (!$res->status)return $res;
            $gasPrice =$res->data->toHex();


            // eth_estimateGas
            $res=$this->eth_estimateGas($fromAddress,$toAddress,$amount);
            if (!$res->status)return $res;
            $gasLimit=$res->data->toHex();

            // 设置交易参数
            $txParams = [
                'from' => $fromAddress,
                'to' => $toAddress,
                'value' =>'0x'. Utils::toWei($amount ,'ether')->toHex(), // 将 ETH 转换为 wei
                'gasPrice' => '0x'. $gasPrice,
                'nonce' => '0x'. $nonce,
                'gasLimit' => '0x'. $gasLimit,
                'chainId' =>'0x'.  Utils::toHex($this->chId),
            ];
            if ($txParams['nonce']=='0x')$txParams['nonce']='0x0';

            $transaction = new Transaction ($txParams['nonce'], $txParams['gasPrice'], $txParams['gasLimit'], $txParams['to'], $txParams['value']);
            $signedTx='0x'. $transaction->getRaw ($fromPrivateKey,$this->chId);

            $res=self::reFail();
            // 发送已签名的交易
            $this->eth->sendRawTransaction($signedTx,function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });
            if (!$res->status)return $res;

            return $res;
        }catch (\Throwable $e){
            // /var/www/html/hyf/vendor/kornrunner/ethereum-offline-raw-tx/src/Ethereum/Transaction.php
            return self::reFail($e->getMessage())->setCode('500');
        }
    }

    /**
     * 发起erp20交易
     * @param $contractAddress
     * @param $fromAddress
     * @param $fromPrivateKey
     * @param $toAddress
     * @param $amount
     * @return Re
     */
    public function transferToken($contractAddress, $fromAddress,$fromPrivateKey, $toAddress, $amount):Re{
        try {
            $abi = '[{"constant":false,"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]';

            $contract = new Contract($this->eth->getProvider(), json_decode($abi, true));


            $decimalsRe=$this->contract_decimals($contractAddress);
            if (!$decimalsRe->status)return $decimalsRe;

            $unitVal='1'.str_repeat('0',(int)$decimalsRe->data);
            $unit=array_search($unitVal,Utils::UNITS);
            if (!$unit)return self::reFail("unit invalid");

            // 构建交易数据
            $value=Utils::toHex(Utils::toWei($amount,$unit),true);
            $data= $contract->at($contractAddress)->getData('transfer',$toAddress,$value);

            // 获取当前 nonce
            $res=$this->eth_nonce($fromAddress);
            if (!$res->status)return $res;
            $nonce =$res->data->toHex();

            // eth_gasPrice
            $res=$this->eth_gasPrice();
            if (!$res->status)return $res;
            $gasPrice =$res->data->toHex();

            // eth_estimateGas
            $res=$this->eth_estimateGas($fromAddress,$toAddress,'0', '0x'.$data);
            if (!$res->status)return $res;
            $gasLimit=$res->data->toHex();

            // 设置交易参数
            $txParams = [
                'nonce' => '0x'. $nonce,
                'gasPrice' => '0x'. $gasPrice,
                'gasLimit' => '0x'. ($gasLimit+0x2710),
                'to' => $contractAddress,
                'data' => '0x'.$data,
                'chainId' =>'0x'.  Utils::toHex($this->chId),
            ];
            if ($txParams['nonce']=='0x')$txParams['nonce']='0x0';

            $transaction =new Transaction ($txParams['nonce'], $txParams['gasPrice'], $txParams['gasLimit'], $txParams['to'], '0',$txParams['data']);
            $signedTx='0x'. $transaction->getRaw ($fromPrivateKey,$this->chId);

            $res=self::reFail();
            // 发送已签名的交易
            $this->eth->sendRawTransaction($signedTx,function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });
            if (!$res->status)return $res;

            return $res;
        }catch (\Throwable $e){
            return self::reFail($e->getMessage())->setCode('500');
        }
    }

    /**
     * 获取hash交易状态
     * @param string $hash
     * @return Re
     */
    public function tansStatus(string $hash):Re{
        $fRe=$this->eth_getTransactionReceipt($hash,true);
        if (!$fRe->status){
            $fRe->data='0x0';
            return $fRe;
        }
        return self::re($fRe->data->status??'0x0');
    }

    /**
     * 获取交易详情
     * @param string $hash
     * @return Re
     */
    public function tansHashInfo(string $hash):Re{
        try {
            $re=$this->eth_getTransactionByHash($hash);
            if (!$re->status)return $re;
            $re->data->status=0;
            $re->data->contractAddress='';
            $re->data->failAddress='';
            $re->data->cumulativeGasUsed=0;
            $re->data->effectiveGasPrice=0;
            $re->data->gasUsed=0;
            $re->data->transactionFee='0.0';
            $re->data->getTransactionReceipt=new \stdClass();

            $fRe=$this->eth_getTransactionReceipt($hash);
            if ($fRe->status){
                $re->data->getTransactionReceipt=$fRe->data;
                if ($fRe->data->contractAddress)$re->data->contractAddress=$fRe->data->contractAddress;

                $re->data->cumulativeGasUsed=$fRe->data->cumulativeGasUsed;
                $re->data->effectiveGasPrice=$fRe->data->effectiveGasPrice;
                $re->data->status=$fRe->data->status;
                if ($fRe->data->logsTo)$re->data->failAddress=$fRe->data->logsTo;
                $re->data->gasUsed=$fRe->data->gasUsed;
                if ($fRe->data->contractAddress){
                    $re->data->value=$fRe->data->value;
                }
                if ($re->data->gasPrice&&$fRe->data->gasUsed){
                    $transactionFee=$re->data->gasPrice*$fRe->data->gasUsed;
                    $re->data->transactionFee=self::weiToNumber(Utils::toHex($transactionFee),18);
                }
            }
            return $re;
        }catch (\Throwable $e){
            return self::reFail($e->getMessage())->setCode('500');
        }
    }


    /**
     * hash交易信息
     * @param string $hash
     * @return Re
     */
    public function eth_getTransactionByHash(string $hash):Re{
        try {
            $res=self::reFail();
            $this->eth->getTransactionByHash($hash,function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });
            if (!$res->status)return $res;

            if (is_null($res->data))return self::reFail()->setCode('404');
            $res->data->blockNumber=hexdec($res->data->blockNumber);
            $res->data->gas=hexdec($res->data->gas);
            $res->data->gasPrice=hexdec($res->data->gasPrice);
            $res->data->nonce=hexdec($res->data->nonce);
            $res->data->transactionIndex=hexdec($res->data->transactionIndex);
            $res->data->value=self::weiToNumber($res->data->value,18);
            $res->data->type=hexdec($res->data->type);
            $res->data->chainId=hexdec($res->data->chainId);

            return $res;
        }catch (\Throwable $e){
            return self::reFail($e->getMessage())->setCode('500');
        }
    }

    /**
     * hash交易状态
     * @param string $hash
     * @param bool $org
     * @return Re
     */
    public function eth_getTransactionReceipt(string $hash,bool $org=false):Re{
        try {
            $res=self::reFail();
            $this->eth->getTransactionReceipt($hash,function ($err,$re)use(&$res){
                $res=self::callBack($err,$re);
            });
            if (!$res->status)return $res;
            if ($org)return $res;

            if (is_null($res->data))return self::reFail()->setCode('404');

            $logsOne= $res->data->logs[0]??null;
            $res->data->blockNumber=hexdec($res->data->blockNumber);
            $res->data->cumulativeGasUsed=hexdec($res->data->cumulativeGasUsed);
            $res->data->effectiveGasPrice=hexdec($res->data->effectiveGasPrice);
            $res->data->gasUsed=hexdec($res->data->gasUsed);
            $res->data->status=hexdec($res->data->status);
            $res->data->transactionIndex=hexdec($res->data->transactionIndex);
            $res->data->type=hexdec($res->data->type);
            $res->data->transactionIndex=0;
            $res->data->value='';
            $res->data->logsTo='';
            $res->data->logIndex=null;

            if (isset($logsOne->address)){
                $res->data->contractAddress=null;
                $re=$this->eth_getTransactionReceiptLogHandle($logsOne);
                if (!$re->status)return $re;
                $res->data->value=$re->data->data;
                $res->data->transactionIndex=$re->data->transactionIndex;
                $res->data->logIndex=$re->data->logIndex;
                $res->data->contractAddress=$re->data->address??null;
                $topics=$re->data->topics??[];
                if (!empty($topics)){
                    $res->data->logsTo=array_pop($topics);
                }

                if ( $res->data->logsTo==$res->data->to){
                    $res->data->logsTo='';
                    $res->data->value='';
                    $res->data->contractAddress=null;
                }
            }

            return $res;

        }catch (\Throwable $e){
            return self::reFail($e->getMessage())->setCode('500');
        }
    }

    /**
     * @param $logOne
     * @return Re
     */
    private  function eth_getTransactionReceiptLogHandle($logOne):Re {
        try {
            if (empty($logOne)||!$logOne||!isset($logOne->address))return self::re();

            $topics=$logOne->topics??[];
            $tmpTopics=[];
            foreach ($topics as $key=>$v){
                if ($key==0){
                    $tmpTopics[]=$v;
                    continue;
                }
                $tmpTopics[]=$this->logs_handleAddress($v);
            }
            $logOne->topics=$tmpTopics;

            if (strlen( $logOne->data)==66){
                $decimalsRe=$this->contract_decimals($logOne->address);
                if (!$decimalsRe->status)return self::reFail("get contract_decimals fail")->setCode('500');
                $logOne->data=self::weiToNumber($logOne->data, (int)$decimalsRe->data);
            }

            $logOne->blockNumber=hexdec($logOne->blockNumber);
            $logOne->transactionIndex=hexdec($logOne->transactionIndex);
            $logOne->logIndex=hexdec($logOne->logIndex);

            return self::re($logOne);

        }catch (\Throwable $e){
            return self::reFail($e->getMessage())->setCode('500');
        }
    }

    /**
     * eth_getTransactionReceiptLogHandle 处理logs 中的钱包地址
     * @param string $address
     * @return string
     */
    private static function logs_handleAddress(string $address):string {
        $prefix=substr($address,0,2);
        if ($prefix=='0x'){
            $stringLength = strlen($address);
            return '0x'.substr($address, $stringLength - 40);
        }
        return $address;
    }

    // composer require kornrunner/ethereum-offline-raw-tx
    // composer require sc0vu/web3.php
}